use std::{cell::RefCell, mem::transmute};
use windows::{
    Win32::{
        Foundation::{self, E_INVALIDARG, E_NOTIMPL, S_OK},
        Graphics::Gdi,
        Security::{
            Authentication::Identity::{
                LSA_STRING, LsaConnectUntrusted, LsaDeregisterLogonProcess,
                LsaLookupAuthenticationPackage, MSV1_0_PACKAGE_NAME,
            },
            Credentials::CRED_PACK_PROTECTED_CREDENTIALS,
        },
        System::Com::CoTaskMemAlloc,
        UI::Shell::{
            CPFIS_FOCUSED, CPFS_DISPLAY_IN_BOTH, CPGSR_RETURN_CREDENTIAL_FINISHED, CPSI_NONE,
            CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION,
            CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE, CREDENTIAL_PROVIDER_FIELD_STATE,
            CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE, CREDENTIAL_PROVIDER_STATUS_ICON,
            ICredentialProviderCredential, ICredentialProviderCredential_Impl,
        },
    },
    core::{PCWSTR, PWSTR},
};

use windows::core::implement;

use image::DynamicImage;
use nokhwa::{
    Camera,
    pixel_format::RgbFormat,
    utils::{CameraIndex, RequestedFormat, RequestedFormatType},
};

use crate::{WCP_CLSID, create_wide_str_ptr, credentials::types::AuthFieldId, windbg_print};

#[implement(ICredentialProviderCredential)]
pub struct Tile {
    username: RefCell<String>,
    password: RefCell<String>,
}

impl Tile {
    fn capture_photo() {
        let native = nokhwa::native_api_backend().unwrap();
        let info = nokhwa::query(native).unwrap();
        println!("devices: {:?}", info);
        if info.len() == 0 {
            return;
        }
        // first camera in system
        let index = CameraIndex::Index(0);
        // request the absolute highest resolution CameraFormat that can be decoded to RGB.
        let requested =
            RequestedFormat::new::<RgbFormat>(RequestedFormatType::AbsoluteHighestFrameRate);
        // make the camera
        let mut camera = Camera::new(index, requested).unwrap();

        // get a frame
        let frame = camera.frame().unwrap();
        println!("Captured Single Frame of {}", frame.buffer().len());
        // decode into an ImageBuffer
        let decoded = frame.decode_image::<RgbFormat>().unwrap();
        println!("Decoded Frame of {}", decoded.len());
        DynamicImage::ImageRgb8(decoded).save("C:/xxx.png").unwrap();
    }
}

impl Default for Tile {
    fn default() -> Self {
        Tile {
            username: String::from("username").into(),
            password: String::from("password").into(),
        }
    }
}

impl ICredentialProviderCredential_Impl for Tile_Impl {
    fn Advise(
        &self,
        _pcpce: windows_core::Ref<windows::Win32::UI::Shell::ICredentialProviderCredentialEvents>,
    ) -> windows_core::Result<()> {
        windbg_print("CredProviderCred : Advise \n");
        Err(E_NOTIMPL.into())
    }

    fn UnAdvise(&self) -> windows_core::Result<()> {
        windbg_print("CredProviderCred : UnAdvise \n");
        Err(E_NOTIMPL.into())
    }

    fn SetSelected(&self) -> windows_core::Result<windows_core::BOOL> {
        windbg_print("CredProviderCred : SetSelected \n");
        Err(E_NOTIMPL.into())
    }

    fn SetDeselected(&self) -> windows_core::Result<()> {
        windbg_print("CredProviderCred : SetDeSelected \n");
        Err(E_NOTIMPL.into())
    }

    fn GetFieldState(
        &self,
        dwfieldid: u32,
        pcpfs: *mut CREDENTIAL_PROVIDER_FIELD_STATE,
        pcpfis: *mut CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE,
    ) -> windows_core::Result<()> {
        windbg_print(
            format!(
                "CredProviderCred : Get field state index = {} \n",
                dwfieldid
            )
            .as_str(),
        );
        unsafe { *pcpfis = CPFIS_FOCUSED };
        unsafe { *pcpfs = CPFS_DISPLAY_IN_BOTH };
        Ok(())
    }

    fn GetStringValue(&self, dwfieldid: u32) -> windows_core::Result<windows_core::PWSTR> {
        windbg_print(
            format!("CredProviderCred : GetStringValue index = {} \n", dwfieldid).as_str(),
        );

        match dwfieldid {
            0 => Ok(PWSTR::from_raw(create_wide_str_ptr("Credential Provider")?)),
            2 => Ok(PWSTR::from_raw(create_wide_str_ptr("Username")?)),
            3 => Ok(PWSTR::from_raw(create_wide_str_ptr("Password")?)),
            _ => Err(E_INVALIDARG.into()),
        }
    }
    // 获取图像信息
    fn GetBitmapValue(&self, _dwfieldid: u32) -> windows_core::Result<Gdi::HBITMAP> {
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn GetCheckboxValue(
        &self,
        _dwfieldid: u32,
        _pbchecked: *mut windows_core::BOOL,
        _ppszlabel: *mut windows_core::PWSTR,
    ) -> windows_core::Result<()> {
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn GetSubmitButtonValue(&self, dwfieldid: u32) -> windows_core::Result<u32> {
        windbg_print(
            format!(
                "CredProviderCred : Get SUbmit button value = {} \n",
                dwfieldid
            )
            .as_str(),
        );
        Tile::capture_photo();
        if dwfieldid == AuthFieldId::SubmitButton as u32 {
            Ok(AuthFieldId::Password as u32)
        } else {
            Err(E_INVALIDARG.into())
        }
    }

    fn GetComboBoxValueCount(
        &self,
        _dwfieldid: u32,
        _pcitems: *mut u32,
        _pdwselecteditem: *mut u32,
    ) -> windows_core::Result<()> {
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn GetComboBoxValueAt(
        &self,
        _dwfieldid: u32,
        _dwitem: u32,
    ) -> windows_core::Result<windows_core::PWSTR> {
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn SetStringValue(
        &self,
        dwfieldid: u32,
        psz: &windows_core::PCWSTR,
    ) -> windows_core::Result<()> {
        match dwfieldid {
            2 => {
                *self.username.borrow_mut() = unsafe { psz.to_string().unwrap() };
            }
            3 => {
                *self.password.borrow_mut() = unsafe { psz.to_string().unwrap() };
            }
            _ => {
                return Err(E_INVALIDARG.into());
            }
        }
        Ok(())
    }

    fn SetCheckboxValue(
        &self,
        dwfieldid: u32,
        bchecked: windows_core::BOOL,
    ) -> windows_core::Result<()> {
        let _ = bchecked;
        let _ = dwfieldid;
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn SetComboBoxSelectedValue(
        &self,
        dwfieldid: u32,
        dwselecteditem: u32,
    ) -> windows_core::Result<()> {
        let _ = dwselecteditem;
        let _ = dwfieldid;
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn CommandLinkClicked(&self, dwfieldid: u32) -> windows_core::Result<()> {
        let _ = dwfieldid;
        windbg_print(format!(" {} \n", std::line!()).as_str());
        todo!()
    }

    fn GetSerialization(
        &self,
        pcpgsr: *mut CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE,
        pcpcs: *mut CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION,
        ppszoptionalstatustext: *mut windows_core::PWSTR,
        pcpsioptionalstatusicon: *mut CREDENTIAL_PROVIDER_STATUS_ICON,
    ) -> windows_core::Result<()> {
        windbg_print("CredProviderCred : GetSerialization \n");
        unsafe {
            *pcpsioptionalstatusicon = CPSI_NONE;
        }

        unsafe {
            *ppszoptionalstatustext = PWSTR::null();
        }

        unsafe {
            *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
        }
        let size: *mut u32 = unsafe { transmute(&u32::default()) };
        let serialized_creds = unsafe { CoTaskMemAlloc(1000) as *mut u8 };
        let status = unsafe {
            windows::Win32::Security::Credentials::CredPackAuthenticationBufferW(
                CRED_PACK_PROTECTED_CREDENTIALS,
                PCWSTR::from_raw(create_wide_str_ptr(self.username.borrow().as_str())?),
                PCWSTR::from_raw(create_wide_str_ptr(self.password.borrow().as_str())?),
                Some(serialized_creds),
                size,
            )
        };

        windbg_print(
            format!(
                "CredProviderCred : CredPackAuthenticationBufferA status = {:?}, size = {:?}\n",
                status,
                unsafe { *size }
            )
            .as_str(),
        );

        unsafe {
            (*pcpcs).ulAuthenticationPackage = retrieve_negotiate_auth_package().unwrap();
            (*pcpcs).cbSerialization = *size;
            (*pcpcs).clsidCredentialProvider = WCP_CLSID;
            (*pcpcs).rgbSerialization = serialized_creds;
        }

        Err(S_OK.into())
    }

    fn ReportResult(
        &self,
        ntsstatus: Foundation::NTSTATUS,
        ntssubstatus: Foundation::NTSTATUS,
        ppszoptionalstatustext: *mut windows_core::PWSTR,
        pcpsioptionalstatusicon: *mut CREDENTIAL_PROVIDER_STATUS_ICON,
    ) -> windows_core::Result<()> {
        let _ = pcpsioptionalstatusicon;
        let _ = ppszoptionalstatustext;
        windbg_print(
            format!(
                "CredProviderCred : ReportResult status = {:?}, substatus = {:?} \n",
                ntsstatus, ntssubstatus
            )
            .as_str(),
        );
        Err(S_OK.into())
    }
}

fn retrieve_negotiate_auth_package() -> ::windows::core::Result<u32> {
    let mut lsahandle = Foundation::HANDLE::default();
    let _ = unsafe { LsaConnectUntrusted(std::ptr::addr_of_mut!(lsahandle)) };
    let package = unsafe { MSV1_0_PACKAGE_NAME.to_string().unwrap() };
    let auth_package: LSA_STRING = LSA_STRING {
        Length: package.len() as u16,
        MaximumLength: package.len() as u16,
        Buffer: unsafe { transmute(package.as_ptr()) },
    };
    let auth_package_ptr: *const LSA_STRING = unsafe { transmute(&auth_package) };
    let auth_id: *mut u32 = unsafe { transmute(&u32::default()) };
    let status = unsafe { LsaLookupAuthenticationPackage(lsahandle, auth_package_ptr, auth_id) };
    windbg_print(
        format!(
            "CredProviderCred : LsaLookupAuthenticationPackage status = {:?}, package = {:?}\n",
            status,
            unsafe { *auth_id }
        )
        .as_str(),
    );

    let _ = unsafe { LsaDeregisterLogonProcess(lsahandle) };
    Ok(unsafe { *auth_id })
}
